module img_data_pkt #(
parameter  
    IMG_FRAME_HEAD = {32'hf0_5a_a5_0f},
    CMOS_H_PIXEL = 16'd640,  // image horizontal direction pixels number
    CMOS_V_PIXEL = 16'd480   // image vertical direction pixels number
    
)(
    input               clk             ,
    input               rst_n           ,
    input       [7:0]   img_data_i      ,
    input               img_href_i      ,   // image data valid enable
    input               img_vsync_i     ,   // vertical sync signal
    input               init_done       ,   // 初始化结束后开始发送数据
    // udp
    input               udp_tx_clk      , 
    input               fifo_rd_en      ,
    output reg          udp_tx_start    ,
    output      [7:0]   udp_tx_data     , 
    output reg  [15:0]  udp_tx_byte_num    // to be sent data's length        
);

// reg define
reg [7:0]   img_data, img_data_i_d1, img_data_i_d2;
reg         img_href, img_href_i_d1, img_href_i_d2;
reg         img_vsync, img_vsync_i_d1, img_vsync_i_d2;
reg         fifo_wr_en;
reg         img_vsync_d1, img_href_d1;
reg         first_frame_time;           // 在vsync的上升沿到第一个href的下降沿 持续为高
reg [7:0]   fifo_wr_data;
reg [3:0]   cnt_first_frame;      // 帧头写入寄存器
reg         data_send_en;
reg         fifo_wr_en_d1;
reg [15:0]  href_cnt;
// wire define
wire        pos_img_vsync;
wire        neg_img_vsync;
wire        neg_img_href;
wire        pos_img_href;
wire [10:0] rd_data_count;
/************************************************************
                            main code
************************************************************/
// 先打拍，防止亚稳态
always @(posedge clk or negedge rst_n) begin 
    if(~rst_n)begin
        img_data_i_d1   <= 8'd0;
        img_data_i_d2   <= 8'd0;
        img_href_i_d1   <= 1'd0;
        img_href_i_d2   <= 1'd0;
        img_vsync_i_d1  <= 1'd0;
        img_vsync_i_d2  <= 1'd0;
        img_data        <= 8'd0;
        img_href        <= 1'd0;
        img_vsync       <= 1'd0;
    end
    else begin
        img_data_i_d1  <= img_data_i;
        img_data_i_d2  <= img_data_i_d1;
        img_data       <= img_data_i_d2;

        img_href_i_d1  <= img_href_i;
        img_href_i_d2  <= img_href_i_d1;
        img_href       <= img_href_i_d2;

        img_vsync_i_d1 <= img_vsync_i;
        img_vsync_i_d2 <= img_vsync_i_d1;
        img_vsync      <= img_vsync_i_d2;
    end
end

// 写fifo数据，并且发送字节长度数据。

// 发送字节长度数据
assign pos_img_vsync = img_vsync & (~img_vsync_d1); 
assign neg_img_vsync = (~img_vsync) & img_vsync_d1; // 获取第一行帧的时间first_frame_time，发送字节长度1288字节，其他时间为1280字节。
assign neg_img_href = (~img_href) & img_href_d1; 
assign pos_img_href = img_href & (~img_href_d1); 

// href_cnt
always @(posedge clk or negedge rst_n) begin 
    if(~rst_n)
        href_cnt <= 16'd0;
    else if(pos_img_vsync)
        href_cnt <= 16'd0;
    else if(pos_img_href)
        href_cnt <= href_cnt + 16'd1;
    else
        href_cnt <= href_cnt;
end

// img_vsync_d1 & img_href_d1
always @(posedge clk or negedge rst_n) begin 
    if(~rst_n)begin
        img_vsync_d1 <= 1'd0;
        img_href_d1 <= 1'd0;
    end
    else begin
        img_vsync_d1 <= img_vsync;
        img_href_d1 <= img_href;
    end
end

// first_frame_time
always @(posedge clk or negedge rst_n) begin 
    if(~rst_n)
        first_frame_time <= 1'd0;
    else begin
        if(neg_img_vsync)
            first_frame_time <= 1'd1;
        else ;
        if(href_cnt == 16'd2)
            first_frame_time <= 1'd0;
        else ;
    end
end

// 输出字节长度
always @(posedge clk or negedge rst_n) begin 
    if(~rst_n)
        udp_tx_byte_num <= 16'd0;
    else if(first_frame_time)
        udp_tx_byte_num <= 16'd1288;
    else
        udp_tx_byte_num <= 16'd1280;
end

// 初始化结束后，写fifo数据，即控制fifo_wr_en和fifo_wr_data这两个信号

always @(posedge clk or negedge rst_n) begin // 等待initialization结束
    if(~rst_n)
        data_send_en <= 1'd0;
    else
        if(init_done)
            data_send_en <= 1'd1;
        else ;
end

always @(posedge clk or negedge rst_n) begin // cnt_first_frame会在每个vsync的结尾处从0计数到8，其他时间保持为9。
    if(~rst_n)
        cnt_first_frame <= 4'd10;
    else begin
        if(neg_img_vsync)
            cnt_first_frame <= 4'd0;
        else ;
        if(cnt_first_frame < 4'd10)
            cnt_first_frame <= cnt_first_frame + 4'd1;
        else ;
    end
end

always @(posedge clk or negedge rst_n) begin // 写帧头（为了方便上位机识别数据，从而显示正确的图像），在vsync为高期间写8个字节的帧头，花9个Tcam_clk
    if(~rst_n)begin
        fifo_wr_en <= 1'd0;
        fifo_wr_data <= 8'd0;
    end
    else begin
        if(data_send_en)begin
            case (cnt_first_frame)
                4'd0: ;
                4'd1: begin
                    fifo_wr_en <= 1'd1;
                    fifo_wr_data <= IMG_FRAME_HEAD[31:24];
                end
                4'd2: fifo_wr_data <= IMG_FRAME_HEAD[23:16];
                4'd3: fifo_wr_data <= IMG_FRAME_HEAD[15: 8];
                4'd4: fifo_wr_data <= IMG_FRAME_HEAD[ 7: 0];
                4'd5: fifo_wr_data <= CMOS_H_PIXEL[15: 8];
                4'd6: fifo_wr_data <= CMOS_H_PIXEL[ 7: 0];
                4'd7: fifo_wr_data <= CMOS_V_PIXEL[15: 8];
                4'd8: fifo_wr_data <= CMOS_V_PIXEL[ 7: 0];
                4'd9: fifo_wr_en <= 1'd0;
                default: begin
                    if(img_href)begin                       // 写数据
                        fifo_wr_en <= 1'd1;
                        fifo_wr_data <= img_data;
                    end
                    else begin
                        fifo_wr_en <= 1'd0;
                        fifo_wr_data <= 8'd0;
                    end
                end
            endcase
        end
        else ;
    end
end

cam_fifo_2048x8_2048x8 u_cam_fifo (
    .rst((~rst_n) | pos_img_vsync),   // input wire rst
    // .rst(~rst_n),   // input wire rst
    .wr_clk(clk),                     // input wire wr_clk
    .rd_clk(udp_tx_clk),              // input wire rd_clk
    .din(fifo_wr_data),               // input wire [7 : 0] din
    .wr_en(fifo_wr_en),               // input wire wr_en
    .rd_en(fifo_rd_en),               // input wire rd_en
    .dout(udp_tx_data),               // output wire [7 : 0] dout
    .full(),                          // output wire full
    .empty(),                         // output wire empty
    .rd_data_count(rd_data_count),    // output wire [10 : 0] rd_data_count
    .wr_rst_busy(),                   // output wire wr_rst_busy
    .rd_rst_busy()                    // output wire rd_rst_busy
);

// 发送udp数据到上位机
always @(posedge udp_tx_clk or negedge rst_n) begin
    if(~rst_n)
        fifo_wr_en_d1 <= 1'd0;
    else
        fifo_wr_en_d1 <= fifo_wr_en;
end

always @(posedge udp_tx_clk or negedge rst_n) begin
    if(~rst_n)
        udp_tx_start <= 1'd0;
    else begin
        if(rd_data_count == udp_tx_byte_num)
            udp_tx_start <= 1'd1;
        else
            udp_tx_start <= 1'd0; 
    end
end

endmodule